<?php

/*
 * Copyright 2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#namespace tehframework;

class Inflector
{
	static private $inflections = array
	(
		'plural' => array
		(
			'/(quiz)$/i'               => '\1zes',
			'/^(ox)$/i'                => '\1en',
			'/([m|l])ouse$/i'          => '\1ice',
			'/(matr|vert|ind)ix|ex$/i' => '\1ices',
			'/(x|ch|ss|sh)$/i'         => '\1es',
			'/([^aeiouy]|qu)ies$/i'    => '\1y',
			'/([^aeiouy]|qu)y$/i'      => '\1ies',
			'/(hive)$/i'               => '\1s',
			'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
			'/sis$/i'                  => 'ses',
			'/([ti])um$/i'             => '\1a',
			'/(buffal|tomat)o$/i'      => '\1oes',
			'/(bu)s$/i'                => '\1ses',
			'/(alias|status)/i'        => '\1es',
			'/(octop|vir)us$/i'        => '\1i',
			'/(ax|test)is$/i'          => '\1es',
			'/s$/i'                    => 's',
			'/$/'                      => 's'
		),
		'singular' => array
		(
			'/(quiz)zes$/i'         => '\\1',
			'/(matr)ices$/i'        => '\\1ix',
			'/(vert|ind)ices$/i'    => '\\1ex',
			'/^(ox)en/i'            => '\\1',
			'/(alias|status)es$/i'  => '\\1',
			'/([octop|vir])i$/i'    => '\\1us',
			'/(cris|ax|test)es$/i'  => '\\1is',
			'/(shoe)s$/i'           => '\\1',
			'/(o)es$/i'             => '\\1',
			'/(bus)es$/i'           => '\\1',
			'/([m|l])ice$/i'        => '\\1ouse',
			'/(x|ch|ss|sh)es$/i'    => '\\1',
			'/(m)ovies$/i'          => '\\1ovie',
			'/(s)eries$/i'          => '\\1eries',
			'/([^aeiouy]|qu)ies$/i' => '\\1y',
			'/([lr])ves$/i'         => '\\1f',
			'/(tive)s$/i'           => '\\1',
			'/(hive)s$/i'           => '\\1',
			'/([^f])ves$/i'         => '\\1fe',
			'/(^analy)ses$/i'       => '\\1sis',
			'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i'
			                        => '\\1\\2sis',
			'/([ti])a$/i'           => '\\1um',
			'/(n)ews$/i'            => '\\1ews',
			'/s$/i'                 => ''
		),
		'irregular'   => array
		(
			'person' => 'people',
			'man'    => 'men',
			'child'  => 'children',
			'sex'    => 'sexes',
			'move'   => 'moves'
		),
		'uncountable' => array
		(
			'equipment',
			'information',
			'rice',
			'money',
			'species',
			'series',
			'fish',
			'sheep'
		)
	);
	
	/**
	 * example -> examples
	 */
	public static function pluralize($word)
	{
		$lowercased = strtolower($word);
		
		foreach (self::$inflections['uncountable'] AS $uncountable)
		{
			if (substr($lowercased, -1 * strlen($uncountable)) == $uncountable)
			{
				return $word;
			}
		}
		
		foreach (self::$inflections['irregular'] AS $plural => $singular)
		{
			$match = array();
			
			if (preg_match('/(' . $plural . ')$/i', $word, $match))
			{
				return preg_replace('/(' . $plural . ')$/i',
					substr($match[0], 0, 1) . substr($singular, 1), $word);
			}
		}
		
		foreach (self::$inflections['plural'] AS $pattern => $replacement)
		{
			if (preg_match($pattern, $word))
			{
				return preg_replace($pattern, $replacement, $word);
			}
		}
		
		return $word;
	}
	
	/**
	 * examples -> example
	 */
	public static function singularize($word)
	{
		$lowercased = strtolower($word);
		
		foreach (self::$inflections['uncountable'] AS $uncountable)
		{
			if (substr($lowercased, -1 * strlen($uncountable)) == $uncountable)
			{
				return $word;
			}
		}
		
		foreach (self::$inflections['irregular'] AS $plural => $singular)
		{
			$match = array();
			
			if (preg_match('/(' . $plural . ')$/i', $word, $match))
			{
				return preg_replace('/(' . $plural . ')$/i',
					substr($match[0], 0, 1) . substr($singular, 1), $word);
			}
		}
		
		foreach (self::$inflections['singular'] AS $pattern => $replacement)
		{
			if (preg_match($pattern, $word))
			{
				return preg_replace($pattern, $replacement, $word);
			}
		}
		
		return $word;
	}
	
	public static function conditionalPlural($count, $word)
	{
		return $count > 1 ? self::pluralize($word) : $word;
	}
	
	/**
	 * this_is_a_simple_example => ThisIsASimpleExample
	 */
	public static function camelize($word)
	{
		$match = array();
		
		if (preg_match_all('/\/(.?)/', $word, $match))
		{
			foreach ($match[1] AS $key => $value)
			{
				$match[1][$key] = '::' . strtoupper($value);
			}
			
			$word = str_replace($match[0], $match[1], $word);
		}
		
		return str_replace(' ', '',
			ucwords(preg_replace('/[^A-Z^a-z^0-9^:]+/', ' ', $word)));
	}
	
	/**
	 * this_is_a_simple_example => thisIsASimpleExample
	 */
	public static function variablize($word)
	{
		$word = self::camelize($word);
		
		return strtolower($word[0]) . substr($word, 1);
	}

	/**
	 * this_is_a_simple_example => this-is-a-simple-example
	 */
	public static function dasherize($word)
	{
		return str_replace('_', '-', $word);
	}
	
	/**
	 * ThisIsASimpleExample => this_is_a_simple_example
	 */
	public static function underscore($word)
	{
		return strtolower(
			preg_replace(
				array(
					'/[^A-Z^a-z^0-9^\/]+/',
					'/([A-Z]+)([A-Z][a-z])/',
					'/([a-z\d])([A-Z])/'),
				array(
					'_',
					'\\1_\\2',
					'\\1_\\2'),
				str_replace(T_NS_SEPARATOR, '/', $word)
			)
		);
	}

	public static function modulize($moduleDescription)
	{
		return self::camelize(self::singularize($moduleDescription));
	}
	
	public static function demodulize($moduleName)
	{
		return self::humanize(
			self::underscore(
				preg_replace('/^.*' . T_NS_SEPARATOR .'/', '', $moduleName)
			)
		);
	}

	public static function foreignKey($className, $separator = '_')
	{
		return self::underscore(self::demodulize($className)) . $separator . 'id';
	}

	public static function tableize($className)
	{
		return self::pluralize(self::underscore($className));
	}

	public static function classify($tableName)
	{
		return self::camelize(self::singularize($tableName));
	}

	public static function humanize($word)
	{
		if (preg_match('/[A-Z]/', $word) && (strrpos($word, '_') === false))
		{
			$word = self::underscore($word);
		}
		
		if (substr($word, -3) === '_id')
		{
			$word = substr($word, 0, -3);
		}
		
		return ucfirst(str_replace('_', ' ', $word));
	}
	
	public static function titleize($word)
	{
		return ucwords(self::humanize(self::underscore($word)));
	}
	
	public static function ordinalize($number)
	{
		if (in_array($number % 100, range(11, 13)))
		{
			return $number . 'th';
		}
		
		$result = (string)$number;
		
		switch ($number % 10)
		{
			case 1:
				$result .= 'st';
				break;
			
			case 2:
				$result .= 'nd';
				break;
			
			case 3:
				$result .= 'rd';
				break;
			
			default:
				$result .= 'th';
				break;
		}
		
		return $result;
	}
}